#include "satellitepos.h"
#include "tleexception.h"
#include "datetime.h"
#include <QFile>        //QFile 属于基础文件处理类
#include <QTextStream>  //文本流操作
#include <QDateTime>
#include <QDebug>
#include <QFileInfo>
#include <QFileInfoList>
#include <QDir>
#include <QMap>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <utility>
#include <clocale>

SatellitePos::SatellitePos(QObject *parent)
    : QObject(parent), m_selectAllMode(false) {
    std::setlocale(LC_ALL, ".UTF-8");  //类构造函数内指定UTF-8
}

QSet<int> SatellitePos::loadNorad(const QString& dirPath) {
    QSet<int> noradSet;
    QDir dir(dirPath);

    // 遍历所有频率文件（例如 9600.txt）
    foreach (const QFileInfo &fileInfo, dir.entryInfoList({"*.txt"}, QDir::Files)) {
        QFile file(fileInfo.absoluteFilePath());
        if (file.open(QIODevice::ReadOnly)) {
            QTextStream in(&file);
            while (!in.atEnd()) {
                bool ok;
                int id = in.readLine().trimmed().toInt(&ok);
                if (ok) noradSet.insert(id);
            }
        }
    }
    return noradSet;
}

void SatellitePos::loadFrequencyNoradMappings(const QString& noradDir) {
    QDir dir(noradDir);
    foreach (const QFileInfo &fileInfo, dir.entryInfoList({"*.txt"}, QDir::Files)) {
        // 从文件名解析中心频率（例如 "9600.txt" -> 9600）
        bool ok;
        int freq = fileInfo.baseName().toInt(&ok);
        if (!ok) {
            qWarning() << "无效的频率文件名:" << fileInfo.fileName();
            continue;
        }

        // 加载文件中的NORAD ID
        QSet<int> noradSet;
        QFile file(fileInfo.absoluteFilePath());
        if (file.open(QIODevice::ReadOnly)) {
            QTextStream in(&file);
            while (!in.atEnd()) {
                int id = in.readLine().trimmed().toInt(&ok);
                if (ok) noradSet.insert(id);
            }
        }
        // 添加到全局 NORAD 集合
        foreach (int id, noradSet) {
            m_noradSet.insert(id);
        }
        m_freqToNorad.insert(freq, noradSet);
        qDebug() << "加载频率" << freq << "对应的NORAD ID数量:" << noradSet.size();
    }
}

QSet<int> SatellitePos::getNoradIdsByFrequency(int freq) const {
    return m_freqToNorad.value(freq);
}

void SatellitePos::loadData(const QString& noradPath,
                            const QString& tlePath,
                            TleSource source,
                            bool selectAllNorad,
                            const QSet<int>& targetIds) {
    m_selectAllMode = selectAllNorad;
    m_tleMap.clear();
    m_freqToNorad.clear(); // 清空旧的频率映射

    // 加载频率到NORAD ID的映射
    if (!m_selectAllMode) {
        loadFrequencyNoradMappings(noradPath); // 新增：加载频率映射
        if (m_freqToNorad.isEmpty()) {
            emit errorOccurred("未找到频率到NORAD ID的映射文件");
            return;
        }
    }


    if (source == LocalFiles) {
        processLocalTle(tlePath, m_noradSet);
    } else {
        QUrl tleUrl("https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=tle");
        // 在线获取TLE的网址，可以修改，目前是取celestack的所有active卫星，至少是缺少FIA的

        QNetworkReply* reply = m_nam.get(QNetworkRequest(tleUrl));
        connect(reply, &QNetworkReply::finished, this, [=]() {
            // QMutexLocker locker(&m_tleMutex); // 在回调开始时加锁
            handleTleDownload(reply);
            reply->deleteLater();
        }, Qt::QueuedConnection);//使用Qt::QueuedConnection确保异步处理
    }
    qDebug() << "已加载的 TLE 数量:" << m_tleMap.size();
}

// 校验和验证函数
bool SatellitePos::validateChecksum(const QString& line) {
    if (line.length() != 69) return false;
    int sum = 0;
    for (int i = 0; i < 68; ++i) {
        QChar c = line[i];
        if (c.isDigit()) sum += c.digitValue();
        else if (c == '-') sum += 1;
    }
    return (sum % 10) == line[68].digitValue();
}

// 解析TLE第二行获取epoch时间（返回年积天+小数）
double SatellitePos::parseEpochFromTleLine2(const QString& line2) {
    if (line2.length() < 32) return 0.0;
    QString epochStr = line2.mid(18, 14).trimmed();
    return epochStr.toDouble();
}

void SatellitePos::handleTleDownload(QNetworkReply* reply) {
    if (reply->error() != QNetworkReply::NoError) {
        emit errorOccurred(QString("网络错误: %1").arg(reply->errorString()));
        reply->deleteLater();
        return;
    }

    QByteArray data = reply->readAll();
    reply->deleteLater();
    QStringList lines = QString::fromUtf8(data).split("\n", Qt::SkipEmptyParts);

    const int totalEntries = lines.size() / 3;
    int processedEntries = 0;

    for (int i = 0; i < lines.size(); ) {
        // 跳过空行
        while (i < lines.size() && lines[i].trimmed().isEmpty()) ++i;
        if (i + 2 >= lines.size()) break;

        // 读取TLE三行数据（名称 + 两行TLE）
        QString name = lines[i++].trimmed();
        QString line1 = lines[i++].trimmed();
        QString line2 = lines[i++].trimmed();

        // 验证TLE格式
        if (line1.length() < 69 || line2.length() < 69) {
            emit errorOccurred(QString("无效的TLE格式: \n%1\n%2").arg(line1).arg(line2));
            continue;
        }

        // 提取NORAD ID
        bool ok;
        int noradId = line2.mid(2, 5).toInt(&ok);
        if (!ok) {
            emit errorOccurred(QString("无效的NORAD ID: %1").arg(line2.mid(2,5)));
            continue;
        }

        // 过滤逻辑（全选模式或存在于NORAD集合）
        if (m_selectAllMode || m_noradSet.contains(noradId)) {
            try {
                auto newTle = std::make_shared<libsgp4::Tle>(
                    name.toStdString(),
                    line1.toStdString(),
                    line2.toStdString()
                    );
                // 解析时间戳（年积天+小数）
                double currentEpoch = parseEpochFromTleLine2(line2);

                bool shouldUpdate = true;

                auto it = m_tleMap.find(noradId);
                if (it != m_tleMap.end()) {
                    QString existingLine2 = QString::fromStdString(it->second.Line2());
                    double existingEpoch = parseEpochFromTleLine2(existingLine2);
                    shouldUpdate = (currentEpoch > existingEpoch);
                }

                // 使用insert_or_assign
                if (shouldUpdate) {
                    //m_tleMap.insert_or_assign(noradId, *newTle);
                    m_tleMap.insert_or_assign(noradId,
                                              libsgp4::Tle(name.toStdString(),
                                                           line1.toStdString(),
                                                           line2.toStdString()));
                }
            }
            catch (const libsgp4::TleException& e) {
                emit errorOccurred(QString("TLE解析错误: %1").arg(e.what()));
            }
            catch (const std::exception& e) {
                emit errorOccurred(QString("系统错误: %1").arg(e.what()));
            }
        }

        emit progressUpdated(++processedEntries * 100 / totalEntries);
    }
    if (m_tleMap.empty()) {
        emit errorOccurred("未找到匹配的TLE数据");
    } else {
        emit dataReady();
    }
    qDebug() << "成功加载的TLE数量:" << m_tleMap.size();
}

bool SatellitePos::processLocalTle(const QString& path, const QSet<int>& noradSet) {
    QFileInfo info(path);
    if (!info.exists()) { // 新增检查
        emit errorOccurred(QString("路径不存在: %1").arg(path));
        return false;
    }
    QStringList tleFiles;

    if (info.isDir()) {
        QDir dir(path);
        tleFiles = dir.entryList({"*.txt"}, QDir::Files);
    } else if (info.isFile() && info.suffix().toLower() == "txt") {
        tleFiles << info.fileName();
    } else {
        emit errorOccurred("无效的TLE路径");
        return false;
    }

    bool hasValidTle = false; // 标记是否成功加载至少一个TLE
;
    foreach (const QString &fileName, tleFiles) {
        QFile file(QDir(info.isDir() ? path : info.path()).filePath(fileName));
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            emit errorOccurred(QString("无法打开文件: %1").arg(file.fileName()));
            continue;
        }

        QTextStream in(&file);

        QString name = "UNKNOWN"; // 默认名称
        QString line1, line2;
        int lineNumber = 0;//实际行号
        int groupLine = 0;//TLE内部行号

        qDebug() << "正在加载TLE文件:" << file.fileName();//添加日志
        while (!in.atEnd()) {
            // 在读取每行时执行：
            QString line = in.readLine()
                               .trimmed()                            // 去除首尾空格
                               .replace(QRegExp("[^\\x20-\\x7E]"), "") // 过滤非 ASCII 可打印字符
                               .left(69)                             // 严格截断
                               .leftJustified(69, ' ');              // 右补空格至 69 字符
            // // 调试输出原始数据
            // qDebug() << "处理后的行长度:" << line.length() << " 内容:" << line;
            // qDebug() << "原始行数据:" << line;
            // qDebug() << "十六进制:" << line.toUtf8().toHex();

            lineNumber++;// 每读取一行即递增行号（包括空行/注释）
            //跳过空行与注释行
            if (line.trimmed().startsWith("#") || line.trimmed().isEmpty()) {
                continue;
            }

            // 自动检测行类型
            TleLineType lineType = detectLineType(line);


            switch (lineType) {
            case NameLine:
                if (groupLine != 0) { // 名称行出现在非起始位置
                    handleIncompleteTle(groupLine, lineNumber);
                }
                if (line.startsWith("0 ")) {
                    name = line.mid(2).trimmed();  // 去除 "0 " 前缀，部分TLE文件以此前缀表明名称行
                    qDebug() << "捕获名称行:" << name;
                } else {
                    name = line.trimmed();
                }
                groupLine = 0;
                break;
            case Line1:
                if (!validateChecksum(line)) {
                    emit errorOccurred(QString("行%1: Line1校验失败").arg(lineNumber));
                    handleIncompleteTle(groupLine, lineNumber);
                    continue;
                }
                if (groupLine != 0) { // 发现新的TLE组
                    handleIncompleteTle(groupLine, lineNumber);
                    groupLine = 0;
                }
                // name = "UNKNOWN";  // 重置名称
                line1 = line.trimmed();
                groupLine = 1;
                break;
            case Line2:
                if (!validateChecksum(line)) {
                    emit errorOccurred(QString("行%1: Line2校验失败").arg(lineNumber));
                    handleIncompleteTle(groupLine, lineNumber);
                    continue;
                }
                if (groupLine != 1) { // 缺少第一行
                    emit errorOccurred(QString("行%1: 意外的第二行").arg(lineNumber));
                    groupLine = 0;
                    continue;
                }
                line2 = line.trimmed();
                groupLine = 2;
                processTleEntry(file.fileName(), lineNumber, name, line1, line2, hasValidTle); // 处理完整TLE
                // name = "UNKNOWN"; // 重置默认名称
                groupLine = 0;
                break;

            default:
                emit errorOccurred(QString("行%1: 无法识别的TLE格式").arg(lineNumber));
                groupLine = 0;
                break;
            }
        }
        // 处理文件末尾未完成的分组
        if (groupLine > 0) {
            handleIncompleteTle(groupLine, lineNumber);
    }
    }
    //最终检查是否加载了至少一个有效TLE
    return hasValidTle || !m_tleMap.empty();//C++ 标准库（STL）中的 std::map 使用小写的 empty() 方法检查容器是否为空,而不是isEmpty()
}

void SatellitePos::handleIncompleteTle(int groupState, int lineNumber) {
    QString msg;
    switch (groupState) {
    case 1: msg = "不完整的TLE组（缺少第二行）"; break;
    case 2: msg = "不完整的TLE组（多余的第二行）"; break;
    }
    emit errorOccurred(QString("行%1: %2").arg(lineNumber).arg(msg));
}

void SatellitePos::processTleEntry(const QString& fileName,
                     int& lineNumber,
                     QString& name,
                     QString& line1,
                     QString& line2,
                     bool& hasValidTle)
{
    try {
        // NORAD ID检查是否在目标库
        const int noradId = line2.mid(2,5).trimmed().toInt();
        if (!m_noradSet.contains(noradId) && !m_selectAllMode) {
            // qDebug() << "跳过未指定的NORAD ID:" << noradId;
            return;
        }
        // 行长度和校验和检查
        if (line1.length() != 69 || line2.length() != 69) {
            throw std::runtime_error("行长度必须为69字符");
        }
        if (!validateChecksum(line1) || !validateChecksum(line2)) {
            throw libsgp4::TleException("校验和验证失败");
        }
        // 调试输出原始数据
        qDebug().nospace()
            << "\n[处理TLE条目] 文件: " << QFileInfo(fileName).fileName()
            << " 第二行的行号: " << lineNumber
            << "\n  名称行: " << (name.isEmpty() ? "<无名称>" : name);
            // << "\n  第一行: " << line1
            // << "\n  第二行: " << line2;

        // // 预处理B*参数
        // QString line1Processed = preprocessBstar(line1);
        // //qDebug() << "  B*预处理结果:" << line1.mid(53,8) << "=>" << line1Processed.mid(53,8);
        // qDebug() << "预处理后的行1长度:" << line1Processed.length()
        //          << " 内容:" << line1Processed;
        // qDebug() << "预处理后的行2长度:" << line2.length()
        //          << " 内容:" << line2;
        // /* B*预处理是完全不必要的，因为构建的libsgp4::tle就是原始TLE，不做处理
        //  * 且如23859-3 => 2.3859e-3这种科学计数法的转换本来就一定会增加字符，
        //  * 用预处理后的行强制验证69逻辑上就完全不对，仅仅存在虚无的调试意义，preprocessBstar函数已删除
        //  * /

        // 验证NORAD ID一致性
        const int line1Norad = line1.mid(2,5).trimmed().toInt();
        const int line2Norad = line2.mid(2,5).trimmed().toInt();
        if(line1Norad != line2Norad) {
            throw libsgp4::TleException("TLE双行的NORAD ID不一致");
        }

        // 字符编码处理
        QByteArray nameUtf8 = name.isEmpty() ? QByteArray("UNKNOWN") : name.toUtf8();
        // qDebug() << "  名称编码验证:"
        //          << "\n    原始长度:" << name.length()
        //          << "\n    UTF-8字节:" << nameUtf8.toHex();

        // std::string line1_std = line1.toStdString();
        // std::string line2_std = line2.toStdString();
        // std::string line1_std = line1.toLatin1().toStdString();
        // std::string line2_std = line2.toLatin1().toStdString();
        QByteArray bytes1 = line1.toLatin1();
        qDebug() << "bytes1内容：" << bytes1.toHex(); // 检查数据是否非空
        QByteArray bytes2 = line2.toLatin1();
        std::string line1_std(bytes1.constData(), bytes1.size());  // 显式指定长度
        std::string line2_std(bytes2.constData(), bytes2.size());  // 显式指定长度，使用该方法，构建的libsgp4::tle可以正确读入
        // 检查字符串是否为纯 ASCII
        if (line1 != line1.toLatin1()) {
            qDebug() << "非 ASCII 字符导致编码扩展,line1："  << line1.toUtf8().toHex(); ;
        }
        else qDebug() << "line1 是纯ASCII 字符" ;
        qDebug() << "  line1 length" << line1.length()
                 <<  "\n line1_std length" << line1_std.length();
        /*
         * 曾测试出一个核心问题：line1 length 69；line1_std length 79，
         * 排除了一切特殊字符问题，只能是特殊隐式转换，或编译器问题
         * 使用Qt Creator 5.15.2,MSVC 2019
         */

        // //输出line1和line1_std的16进制值，观察是否一致
        // qDebug() << "QString hex:" << line1.toUtf8().toHex();
        // // 输出 std::string 的原始字节
        // QByteArray stdBytes(line1_std.c_str(), line1_std.size());
        // qDebug() << "std::string hex:" << stdBytes.toHex();

        // 长度验证
        if (line1_std.length() != 69) {
            throw std::runtime_error("行1长度异常");
        }
        if (line2_std.length() != 69) {
            throw std::runtime_error("行2长度异常");
        }
        // 空值检查
        if (line1_std.find_first_not_of(' ') == std::string::npos ||
            line2_std.find_first_not_of(' ') == std::string::npos) {
            throw std::runtime_error("空行数据");
        }

        // 构建TLE对象
        libsgp4::Tle tle(
            nameUtf8.constData(),
            // line1Processed.toStdString(),
            // line2.toStdString()
            line1_std,
            // line1_origin,
            line2_std
            );

        // 数据存储
        m_tleMap.insert_or_assign(line2Norad, tle);
        hasValidTle = true;

        qDebug() << " 成功加载NORAD:" << line2Norad
                 << " 名称:" << nameUtf8.constData();

    } catch (const libsgp4::TleException& e) {
        qCritical().nospace()
        << "[TLE格式错误] 文件:" << QDir::toNativeSeparators(fileName)
        << " 行:" << (lineNumber-2) << "-" << lineNumber
        << "\n  内容: " << line1 << "\n        " << line2
        << "\n  错误: " << e.what();
        emit errorOccurred(QString("TLE格式错误: %1").arg(e.what()));
    } catch (const std::exception& e) {
        qCritical().nospace()
        << "[系统异常] 文件:" << QDir::toNativeSeparators(fileName)
        << " 行:" << lineNumber
        << "\n  内容: " << line1 << "\n        " << line2
        << "\n  错误: " << e.what();
        emit errorOccurred(QString("系统错误: %1").arg(e.what()));
    }

    // 清理状态
    name.clear();
    line1.clear();
    line2.clear();
}

// 主加载函数
void SatellitePos::loadFiles(const QString& noradDir, const QString& tleDir) {
    m_tleMap.clear(); // 清空旧数据
    m_freqToNorad.clear();

    // 步骤1：加载频率到NORAD的映射
    QDir dir(noradDir);
    foreach (const QFileInfo &fileInfo, dir.entryInfoList({"*.txt"}, QDir::Files)) {
        // 从文件名解析频率（如 9600.txt -> 9600）
        bool ok;
        int freq = fileInfo.baseName().toInt(&ok);
        if (!ok) {
            qWarning() << "跳过无效频率文件:" << fileInfo.fileName();
            continue;
        }

        // 加载该频率对应的NORAD ID
        QSet<int> ids = loadNorad(fileInfo.absoluteFilePath()); // 加载单个文件
        m_freqToNorad.insert(freq, ids);
        qDebug() << "加载频率" << freq << "MHz, 卫星数量:" << ids.size();
    }


    // 步骤2：加载TLE数据
    if (!processLocalTle(tleDir, QSet<int>())) { // 加载所有TLE
        emit errorOccurred("TLE加载失败");
        return;
    }

    // 发送完成信号
    emit progressUpdated(100);
}


libsgp4::DateTime SatellitePos::qdatetimeToSgp4DateTime(const QDateTime& qtTime) const {
    QDateTime utcTime = qtTime.toUTC(); // 确保使用UTC时间

    int year = utcTime.date().year();
    int month = utcTime.date().month();
    int day = utcTime.date().day();
    int hour = utcTime.time().hour();
    int minute = utcTime.time().minute();
    int second = utcTime.time().second();
    int microsecond = utcTime.time().msec() * 1000; // 毫秒转微秒

    return libsgp4::DateTime(
        year, month, day,
        hour, minute, second, microsecond
        );
}

//计算卫星的位置
QHash<int, libsgp4::Eci> SatellitePos::calculatePositions(const QDateTime& qtTime,
                                                          const QSet<int>& targetNoradIds = QSet<int>()//NORAD ID过滤，减少无关卫星的计算
                                                          ) {
    QHash<int, libsgp4::Eci> results;

    const int total = m_tleMap.size();  // 总卫星数量
    // const int MAX_TIME_SPAN_DAYS = 30; // 允许的最大时间跨度（天），可以做修改
    const double EARTH_RADIUS_KM = 6371.0;//地球平均半径
    int processed = 0;                 // 已处理数量

    // 使用C++11范围循环，这里需避免使用foreach，否则会隐性使用默认构造，都是没有默认构造就会报错,需要在程序各处都保持显式构造
    for (const auto& [noradId, tle] : m_tleMap) {
        try {
            libsgp4::SGP4 sgp4(tle);
            libsgp4::DateTime sgp4Time = qdatetimeToSgp4DateTime(qtTime); // 转换 Qt 时间到 SGP4 时间
            libsgp4::TimeSpan duration = sgp4Time - tle.Epoch();
            double tsince = duration.TotalMinutes();
            // 若targetNoradIds非空且当前ID不在集合中，则跳过
            if (!targetNoradIds.isEmpty() && !targetNoradIds.contains(noradId)) {
                continue;
            }

            // if (std::abs(tsince) > 1440 * MAX_TIME_SPAN_DAYS) {
            //     qWarning() << "时间跨度超出范围:" << noradId << tsince << "分钟";
            //     continue;
            // }
            // results.insert(noradId, sgp4.FindPosition(tsince));
            libsgp4::Eci eci = sgp4.FindPosition(tsince);
            results.insert(noradId, eci);

            // // 添加调试输出，卫星较少时可以输出坐标进行验证
            // qDebug() << "卫星 NORAD ID:" << noradId
            //          << "\n  ECI坐标 (km):"
            //          << "\n  X:" << eci.Position().x
            //          << "\n  Y:" << eci.Position().y
            //          << "\n  Z:" << eci.Position().z
            //          << "\n  ECI速度 (km/s):"
            //          << "\n  Vx:" << eci.Velocity().x
            //          << "\n  Vy:" << eci.Velocity().y
            //          << "\n  Vz:" << eci.Velocity().z;
            // 显示地心距离（验证合理性）
            double distance = sqrt(pow(eci.Position().x,2) + pow(eci.Position().y,2) + pow(eci.Position().z,2));
            qDebug() << "卫星" << noradId << "卫星轨道高度:" << distance - EARTH_RADIUS_KM << "km";
        }
        catch (const libsgp4::TleException& e) {
            emit errorOccurred(QString("TLE Error: %1").arg(e.what()));
                qCritical() << "卫星" << noradId << "位置计算失败:" << e.what();
        }
        if (total > 0) {
            emit progressUpdated(++processed * 100 / total);
        } else {
            emit progressUpdated(0);
        }//回避一下除0风险
    }
    return results;
}

